<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1.2-块级声明</title>
</head>
<body>
<script>
    //////////////////////////////////////////  1.2. 块级声明（Block-Level Declarations）  START  /////////////////////////////////////////////
    /*
    * 块级声明指的是该声明的变量无法被代码块外部访问。块作用域，又被称为词法作用域（lexical scopes），可以在如下的条件下创建：

        函数内部
        在代码块（即 { 和 }）内部
        块级作用域是很多类C语言的工作机制，ECMAScript 6 引入块级声明的目的是增强 JavaScript 的灵活性，同时又能与其它编程语言保持一致。
    * */
    // 1.2.1. let 声明（Let Declarations）
    function getValueTest4let(condition) {

        if (condition) {
            let value = "blue";
            console.log(value);
            // 其它代码

            return value;
        } else {

            // value 并不存在（无法访问）
            console.log(value);
            // 使用了没有声明的变量会报错!
            //Uncaught ReferenceError: value is not defined
            //at getValue2 (1.1-Var 声明与变量提升.html?_ijt=1itqit5kqihn4oqbtvs97eotcb:131)
            //at 1.1-Var 声明与变量提升.html?_ijt=1itqit5kqihn4oqbtvs97eotcb:139
            return null;
        }

        // 这里 value 也不存在
    }

    console.group('---------1.2.1. let 声明（Let Declarations）---------');
    getValueTest4let(true);
    getValueTest4let(false);
    console.groupEnd();
    //////////////////////////////////////////  1.2. 块级声明（Block-Level Declarations）  END  /////////////////////////////////////////////

    //////////////////////////////////////////  1.2.2. 禁止重复声明（No Redeclaration）  START  /////////////////////////////////////////////
    console.group('--------- 1.2.2. 禁止重复声明（No Redeclaration）---------');
    /*
    var count = 30;

    // 语法错误
    let count = 40;
    */
    // Uncaught SyntaxError: Identifier 'count' has already been declared
    console.groupEnd();

    /*var count = 30;

    // 不会抛出错误
    if (condition) {

        let count = 40;

        // 其它代码
    }*/
    //////////////////////////////////////////  1.2.2. 禁止重复声明（No Redeclaration）  END  /////////////////////////////////////////////


    //////////////////////////////////////////  1.2.3. const 声明（Constant Declarations）  START  /////////////////////////////////////////////
    /**
     * 在 ECMAScript 6 中也可使用常量（const）语法来声明变量。该种方式声明的变量会被视为常量，这意味着它们不能再次被赋值。由于这个原因，所有的 const 声明的变量都必须在声明处初始化。示例如下：

     // 合法的声明
     const maxItems = 30;

     // 语法错误：未初始化
     const name;

     const 声明 vs let 声明（Constants vs Let Declarations）
     const 和 let 都是块级声明，意味着执行流跳出声明所在的代码块后就没有办法在访问它们，同样 const 变量也不会被提升，示例如下：

     if (condition) {
        const maxItems = 5;

        // 其它代码
    }

     // maxItems 在这里无法访问

     另一处和 let 相同的地方是，const 也不能对已存在的标识符重复定义，不论该标识符由 var（全局或函数级作用域）还是 let （块级作用域）定义。例如以下的代码：

     var message = "Hello!";
     let age = 25;

     // 下面每条语句都会抛出错误
     const message = "Goodbye!";
     const age = 30;

     尽管 const 和 let 使用起来很相似，但是必须要记住它们的根本性的差异：不管是在严格（strict）模式还是非严格模式（non-strict）模式下，const 变量都不允许被重复赋值。

     const maxItems = 5;

     maxItems = 6;      // 抛出错误
     和其它编程语言类似，maxItems 不能被赋予新的值，然而和其它语言不同的是，const 变量的值如果是个对象，那么这个对象本身可以被修改。


     */





    //////////////////////////////////////////  1.2.3. const 声明（Constant Declarations）  END  /////////////////////////////////////////////


    /////////////////////////////////////////  1.2.4 暂存性死区 START ///////////////////////////////////////////
    /**
     * 1.2.4. 暂存性死区（The Temporal Dead Zone）
     let 或 const 声明的变量在声明之前不能被访问。如果执意这么做会出现错误，甚至是 typeof 这种安全调用（safe operations）也不被允许的：

     if (condition) {
            console.log(typeof value);  // ReferenceError!
            let value = "blue";
        }
     在这里，变量 value 由 let 声明并被初始化，但是由于该语句之前抛出了错误导致其从未被执行。这种现象的原因是该语句存在于被 JavaScript 社区泛称为暂存性死区（Temproal Dead Zone, TDZ）之内。
     ECMAScript 规范中未曾对 TDZ 有过显式命名，不过在描述 let 和 const 声明的变量为何在声明前不可访问时，该术语经常被使用。
     本章会涵盖在 TDZ 中关于声明位置的一些微妙部分。此外示例虽然全部用的是 let ，但是实际用法和 const 别无二致。

     当 JavaScript 引擎在作用域中寻找变量声明时，会将变量提升到函数/全局作用域的顶部（var）或是放入 TDZ（let 和const）内部。任何试图访问 TDZ 内部变量的行为都会以抛出运行时（runtime）错误而告终。
     当执行流达到变量声明的位置时，变量才会被移出 TDZ ，代表它们可以被安全使用。

     由 let 或 const 声明的变量，如果你想在定义它们之前就使用，那么以上所述的准则都是铁打不变的。正如之前的示例所演示的那样，typeof 操作符都不能幸免。不
     过，你可以在代码块之外的地方对变量使用 typeof 操作符，但结果可能并不是你想要的。考虑如下的代码：

     console.log(typeof value);     // "undefined"

     if (condition) {
            let value = "blue";
        }
     当对 value 变量使用 typeof 操作符时它并没有处在 TDZ 内部，因为它的位置在变量声明位置所在的代码块之外。这意味着没有发生任何绑定，所以 typeof 仅返回 “undefined”

     TDZ 只是发生在块级绑定中独特的特设定之一，另一个特殊设定发生在循环中。
     * */
    /////////////////////////////////////////  1.2.4 暂存性死区 END ///////////////////////////////////////////
</script>

</body>
</html>